#ifndef __BLOCKQUEUE__
#define __BLOCKQUEUE__

#include "Thread.hpp"
#include <queue>
#include <pthread.h>
template<class T>
class BlockQueue
{
    public:
    BlockQueue(int cap) : _cap(cap)
    {
        pthread_mutex_init(&_comm_mutex,nullptr);
        pthread_cond_init(&_consumer_wait,nullptr);
        pthread_cond_init(&_productor_wait,nullptr);

    }
    ~BlockQueue()
    {
        pthread_mutex_destroy(&_comm_mutex);
        pthread_cond_destroy(&_consumer_wait);
        pthread_cond_destroy(&_productor_wait);
    }
    bool Full()
    {
        return _cap == _bq.size();
    }
    void enqueue(T val)
    {
        pthread_mutex_lock(&_comm_mutex);
        while(Full()) 
        {
            ++_productor_wait_num;
            pthread_cond_wait(&_productor_wait , &_comm_mutex);
            --_productor_wait_num;
        }

        _bq.push(val);

        if(_consumer_wait_num > 0) pthread_cond_signal(&_consumer_wait);

        pthread_mutex_unlock(&_comm_mutex);
    }
    bool Empty()
    {
        return _bq.size() == 0;
    }
    void dequeue(T& val)
    {
        pthread_mutex_lock(&_comm_mutex);
        while(Empty())
        {
            ++_consumer_wait_num;
            pthread_cond_wait(&_consumer_wait,&_comm_mutex);
            --_consumer_wait_num;
        }

        val = _bq.front();
        _bq.pop();
        if(_productor_wait_num > 0) pthread_cond_signal(&_productor_wait);
        pthread_mutex_unlock(&_comm_mutex);
    }
    private:
        std::queue<T> _bq;
        int _cap;

        pthread_mutex_t _comm_mutex;
        pthread_cond_t _consumer_wait;
        pthread_cond_t _productor_wait;
        int _productor_wait_num = 0;
        int _consumer_wait_num = 0;
};
#endif
